#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/spinlock.h>

struct gpio_key{
    int gpio;
    enum of_gpio_flags flags;
    int irq;
};

static struct gpio_key *gpio_key_info;

static irqreturn_t gpio_key_irq_handler(int irq, void* dev_id){
    struct gpio_key *key_get = dev_id;

    int value = gpio_get_value(key_get->gpio);
    if (value < 0) {
        printk(KERN_ERR "[%s] Failed to get GPIO value for key %d, error: %d\n",
               __func__, key_get->gpio, value);
        return IRQ_HANDLED;
    }

    printk(KERN_INFO "[%s]key %d interrupt, the value is %d\n", __func__,
                     key_get->gpio, value);

    return IRQ_HANDLED;
}

static int gpio_key_probe(struct platform_device * device){
    struct device_node *node = device->dev.of_node; 
    int gpio_cont = of_gpio_count(node);

    gpio_key_info = (struct gpio_key *)kzalloc(sizeof(struct gpio_key) * gpio_cont,GFP_KERNEL);
    if (!gpio_key_info) {
        printk(KERN_ERR "[%s]: Failed to allocate memory for gpio_key_info\n", __func__);
        return -ENOMEM;
    }

    int i, irq, gpio, ret;
    enum of_gpio_flags flags;
    for(i = 0; i < gpio_cont; i++){
        gpio = of_get_gpio_flags(node, i, &flags);
        if (gpio < 0) {
            printk(KERN_ERR "[%s]: Failed to get GPIO flags for index %d\n", __func__, i);
            ret = gpio;
            goto err_free_irq;
        }

        irq = gpio_to_irq(gpio);
        if (irq < 0) {
            printk(KERN_ERR "[%s]: Failed to map GPIO %d to IRQ\n", __func__, gpio);
            ret = irq;
            goto err_free_irq;
        }

        ret = request_irq(irq, gpio_key_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, \
                    "fison_gpio_key", &gpio_key_info[i]);
        if (ret) {
            printk(KERN_ERR "[%s]: Failed to request IRQ %d\n", __func__, irq);
            goto err_free_irq;
        }

        /*save gpio infomation to global*/
        gpio_key_info[i].gpio = gpio;
        gpio_key_info[i].flags = flags;
        gpio_key_info[i].irq = irq;
    }

    printk(KERN_INFO "[%s]: Successfully initialized %d GPIO keys\n", __func__, gpio_cont);
    return 0;

    err_free_irq:
        while (--i >= 0) {
            free_irq(gpio_key_info[i].irq, &gpio_key_info[i]);
        }
        kfree(gpio_key_info);
        return ret;
}

static int gpio_key_remove(struct platform_device *device){
    int i;
    struct device_node *node = device->dev.of_node;
    int gpio_cont = of_gpio_count(node);

    if (gpio_key_info) {
        for (i = 0; i < gpio_cont; i++) {
            free_irq(gpio_key_info[i].irq, &gpio_key_info[i]);
        }
        kfree(gpio_key_info);
        gpio_key_info = NULL;
    }

    return 0;
}

static struct of_device_id key_dev_id = {
    .compatible = "fison_gpio_key"
};

static struct platform_driver key_drv = {
    .probe    = gpio_key_probe,
    .remove   = gpio_key_remove,
    .driver   = {
        .name           = "fison_gpio_key_platform",
        .of_match_table = &key_dev_id,
    },
};

static int __init gpio_key_init(void){
    platform_driver_register(&key_drv);
}

static void __exit gpio_key_exit(void){
    platform_driver_unregister(&key_drv);
}

module_init(gpio_key_init);
module_exit(gpio_key_exit);

MODULE_DESCRIPTION("gpio key driver");
MODULE_AUTHOR("fison");
MODULE_LICENSE("GPL");